Stack = Stack or {}

function Stack.new()
    local inst = {}
    inst._cname = "lang.Stack"
    
    local arr = {}
    local len = 0
    
    function inst:push(v)
        len = len + 1
        arr[len] = v
    end
    
    function inst:pop()
        assert(len >= 1, "Stack: pop: out of range")
        local v = arr[len]
        arr[len] = nil
        len = len - 1
        return v
    end
    
    function inst:peek()
        if len >= 1 then
            return arr[len]
        end
        return nil
    end
    
    function inst:count()
        return len
    end
    
    function inst:contains(v)
        for i=1,len do
            if arr[i] == v then
                return true
            end
        end
        return false
    end
    
    function inst:clear()
        for i=1,len do
            arr[i] = nil
        end
        len = 0
    end
    
    function inst:copyTo(dst)
        assert("table" == type(dst), "copyTo: dst not table or Stack")
        if "lang.Stack" == dst._cname then
            for i=1,len do
                dst:push(arr[i])
            end
        else
            for i=1,len do
                table.insert(dst, arr[i])
            end
        end
    end
    
    function inst:__tostring()
        if len <= 0 then return "" end
		local strTb = {}
		for i=1,len do
			local item = arr[i]
			table.insert(strTb, (nil == item) and "nil" or tostring(item))
		end
        return table.concat(strTb, ",")
    end

	function inst:__len()
		return len
	end
    
    function inst:__index(k)
        error("Stack: invalid member: " .. k)
    end
    
    function inst:__newindex(k, v)
        error("Stack: add member error: " .. k)
    end
    
    function inst:__pairs()
        local iterator = function(src, index)
            if nil == index then 
                index = len 
            else
                index = index - 1
            end
            if index >= 1 then
                return index, arr[index]
            end
            return nil, nil
        end
        return iterator
    end
    
    setmetatable(inst, inst)
    return inst
end

Stack.__call = function()
    return Stack.new()
end

setmetatable(Stack, Stack)



function Test1()
    local stack = Stack()
    stack:push("one")
    assert(1 == stack:count(), "push error")
    assert("one" == stack:peek(), "peek error")
    assert("one" == stack:pop(), "pop error")
    assert(0 == stack:count(), "pop error")
    
    assert(nil == stack:peek(), "peek error")
    
    for i=1,100 do
        stack:push(i)
    end
    assert(100 == stack:count(), "push error")
    stack:clear()
    assert(0 == stack:count(), "push error")
    
    stack:push("a")
    stack:push("b")
    
    local s2 = Stack()
    stack:copyTo(s2)
    assert(2 == s2:count(), "copyTo error")
    
    for k,v in s2:__pairs() do
        print("k: " .. k .. ": " .. v)
    end
    
    assert("b" == s2:peek(), "peek error")
    assert("b" == s2:pop(), "pop error")
    
    assert("a" == s2:peek(), "peek error")
    assert("a" == s2:pop(), "pop error")
    
    assert(0 == s2:count(), "copyTo error")
end


--Test1()


